/* @internal */
namespace ts {
    let nextSymbolId = 1;
    let nextNodeId = 1;
    let nextMergeId = 1;
    let nextFlowId = 1;

    export function getNodeId(node: Node): number {
        if (!node.id) {
            node.id = nextNodeId;
            nextNodeId++;
        }
        return node.id;
    }

    export function getSymbolId(symbol: Symbol): number {
        if (!symbol.id) {
            symbol.id = nextSymbolId;
            nextSymbolId++;
        }

        return symbol.id;
    }

    export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker {
        // Cancellation that controls whether or not we can cancel in the middle of type checking.
        // In general cancelling is *not* safe for the type checker.  We might be in the middle of
        // computing something, and we will leave our internals in an inconsistent state.  Callers
        // who set the cancellation token should catch if a cancellation exception occurs, and
        // should throw away and create a new TypeChecker.
        //
        // Currently we only support setting the cancellation token when getting diagnostics.  This
        // is because diagnostics can be quite expensive, and we want to allow hosts to bail out if
        // they no longer need the information (for example, if the user started editing again).
        let cancellationToken: CancellationToken;
        let symbolCount = 0;

        const emptyArray: any[] = [];

        const compilerOptions = host.getCompilerOptions();

        const checker: TypeChecker = {
            getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"),
            getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"),
            getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount") + symbolCount,
            getDiagnostics,
            getGlobalDiagnostics,
            getEmitResolver
        };

        initializeTypeChecker();

        return checker;

        function initializeTypeChecker() {
            // Bind all source files and propagate errors
            forEach(host.getSourceFiles(), file => {
                bindSourceFile(file, compilerOptions);
            });
        }

        function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken) {
            // Ensure we have all the type information in place for this file so that all the
            // emitter questions of this resolver will return the right information.
            getDiagnostics(sourceFile, cancellationToken);
            return {};
        }
        
        function getDiagnosticsWorker(sourceFile: SourceFile): Diagnostic[] {
            return [];
        }

        function getGlobalDiagnostics(): Diagnostic[] {
            return [];
        }
        
        function getDiagnostics(sourceFile: SourceFile, ct: CancellationToken): Diagnostic[] {
            try {
                // Record the cancellation token so it can be checked later on during checkSourceElement.
                // Do this in a finally block so we can ensure that it gets reset back to nothing after
                // this call is done.
                cancellationToken = ct;
                return getDiagnosticsWorker(sourceFile);
            }
            finally {
                cancellationToken = undefined;
            }
        }

    }
}
